import os
import time
from dataclasses import dataclass
from typing import Any, Callable, Dict, Optional

from .logger import log_event


@dataclass
class Decision:
    kind: str  # "allow" | "deny" | "require_approval"
    reason: str = ""


class PolicyEngine:
    """Minimal policy engine: evaluate capability/target/params to a decision.
    Env overrides:
      - SECURITY_MODE: strict|auto|custom (default: auto)
      - REQUIRE_APPROVAL: 0/1 (default: inherited from env)
    """

    HARD_DENY = {"file_delete", "process_kill"}
    DEFAULT_APPROVAL = {"file_write", "process_launch", "screen_capture"}

    def __init__(self):
        self.security_mode = os.environ.get("SECURITY_MODE", "auto").lower()
        self.require_approval = bool(int(os.environ.get("REQUIRE_APPROVAL", os.environ.get("REQUIRE_APPROVAL", "0"))))

    def evaluate(self, capability: str, target: str | None, params: Dict[str, Any] | None) -> Decision:
        cap = (capability or "").lower()
        # Hard deny
        if cap in self.HARD_DENY:
            return Decision("deny", reason=f"capability={cap} hard-deny")
        # Strict mode: broader approval
        if self.security_mode == "strict":
            if cap not in {"input_mouse", "input_keyboard", "window_manage", "vision_wait"}:
                return Decision("require_approval", reason=f"strict-mode approval for {cap}")
        # Auto mode: selected approvals if env requires
        if self.require_approval and cap in self.DEFAULT_APPROVAL:
            return Decision("require_approval", reason=f"env approval for {cap}")
        # Default allow for low-risk capabilities
        return Decision("allow")


class ApprovalManager:
    """Stub approval manager.
    Implementors may connect to UI prompts or CLI confirmation.
    For now, auto-approve based on env AUTO_APPROVE (default true when REQUIRE_APPROVAL=0).
    """

    def __init__(self):
        self.auto_approve = bool(int(os.environ.get("AUTO_APPROVE", "1")))

    def confirm(self, decision: Decision, capability: str, target: Optional[str], params: Optional[Dict[str, Any]]) -> bool:
        try:
            log_event("SECURITY_APPROVAL", f"kind={decision.kind}, cap={capability}, target={target}")
        except Exception:
            pass
        return self.auto_approve


class RollbackManager:
    """Stub rollback manager.
    Prepare a token pre-execution and apply rollback on exception.
    For skeleton, return None (no-op).
    """

    def prepare(self, capability: str, target: Optional[str], params: Optional[Dict[str, Any]]):
        return None

    def apply(self, token: Any):
        return None


class SecurityGateway:
    def __init__(self, policy: PolicyEngine, approval: ApprovalManager, rollback: RollbackManager):
        self.policy = policy
        self.approval = approval
        self.rollback = rollback

    def execute(
        self,
        actor: str,
        capability: str,
        target: Optional[str],
        params: Optional[Dict[str, Any]],
        executor_fn: Callable[[], Any],
    ) -> Dict[str, Any]:
        start = time.time()
        decision = self.policy.evaluate(capability, target, params or {})
        try:
            log_event("SECURITY_DECISION", f"actor={actor}, cap={capability}, target={target}, kind={decision.kind}, reason={decision.reason}")
        except Exception:
            pass
        if decision.kind == "deny":
            return {"ok": False, "result": "denied", "reason": decision.reason}
        if decision.kind == "require_approval":
            if not self.approval.confirm(decision, capability, target, params or {}):
                return {"ok": False, "result": "user_rejected", "reason": decision.reason}
        token = self.rollback.prepare(capability, target, params or {})
        try:
            value = executor_fn()
            latency_ms = int((time.time() - start) * 1000)
            try:
                log_event("SECURITY_EXECUTE", f"cap={capability}, target={target}, result=success, latency_ms={latency_ms}")
            except Exception:
                pass
            return {"ok": True, "value": value, "latency_ms": latency_ms, "rollback_token": token}
        except Exception as e:
            self.rollback.apply(token)
            latency_ms = int((time.time() - start) * 1000)
            try:
                log_event("SECURITY_ROLLBACK", f"cap={capability}, target={target}, error={str(e)}, latency_ms={latency_ms}")
            except Exception:
                pass
            return {"ok": False, "result": "rolled_back", "error": str(e), "latency_ms": latency_ms, "rollback_token": token}


_gateway: Optional[SecurityGateway] = None


def get_gateway() -> SecurityGateway:
    global _gateway
    if _gateway is None:
        _gateway = SecurityGateway(PolicyEngine(), ApprovalManager(), RollbackManager())
    return _gateway